package osmedile.intellij.checkmyvars;

import com.intellij.codeInsight.template.impl.TemplateContext;
import com.intellij.codeInsight.template.impl.TemplateEditorUtil;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.editor.Editor;
import com.intellij.openapi.editor.EditorFactory;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.Nullable;

import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.JTableHeader;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.util.ArrayList;

/**
 * @author Olivier Smedile
 * @version $Id: JHConfig.java 56 2008-07-29 17:04:34Z osmedile $
 */
public class CMVConfig {
    private JPanel rootPanel;
    private JTable varCheckerTable;
    private JButton removeAnnoBt;
    private JButton addAnnoBt;
    private JButton resetDefaultCfgBt;
    private JPanel annosPanel;
    private JCheckBox askToCreateSample;
    private JSplitPane varCheckersSplitPane;
    private JButton editBt;
    private JButton duplicateBt;

    private AnnoTableModel varCheckerTableModel;

    private AddVarCheckerDialog addVarCheckerDialog;

    private Editor templateEditor;

    public String[] COL_TIPS =
            {"<html>If <b>false</b>, this annotation won't be processed</html>",
             "Group name", "Annotation", "Type of variable"};

    private void createUIComponents() {
    }

    {
// GUI initializer generated by IntelliJ IDEA GUI Designer
// >>> IMPORTANT!! <<<
// DO NOT EDIT OR ADD ANY CODE HERE!
        $$$setupUI$$$();
    }

    /**
     * Method generated by IntelliJ IDEA GUI Designer
     * >>> IMPORTANT!! <<<
     * DO NOT edit this method OR call it in your code!
     *
     * @noinspection ALL
     */
    private void $$$setupUI$$$() {
        rootPanel = new JPanel();
        rootPanel.setLayout(new GridBagLayout());
        rootPanel.setMinimumSize(new Dimension(400, 222));
        rootPanel.setPreferredSize(new Dimension(600, 500));
        annosPanel = new JPanel();
        annosPanel.setLayout(new GridBagLayout());
        GridBagConstraints gbc;
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.gridwidth = 5;
        gbc.weightx = 0.1;
        gbc.weighty = 0.3;
        gbc.fill = GridBagConstraints.BOTH;
        gbc.insets = new Insets(10, 0, 10, 0);
        rootPanel.add(annosPanel, gbc);
        annosPanel.setBorder(BorderFactory.createTitledBorder("Annotations"));
        final JPanel spacer1 = new JPanel();
        gbc = new GridBagConstraints();
        gbc.gridx = 5;
        gbc.gridy = 0;
        gbc.weightx = 1.0;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        annosPanel.add(spacer1, gbc);
        varCheckersSplitPane = new JSplitPane();
        varCheckersSplitPane.setDividerSize(9);
        varCheckersSplitPane.setLastDividerLocation(-1);
        varCheckersSplitPane.setMinimumSize(new Dimension(50, 50));
        varCheckersSplitPane.setOrientation(0);
        varCheckersSplitPane.setResizeWeight(0.9);
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 1;
        gbc.gridwidth = 13;
        gbc.weightx = 1.0;
        gbc.weighty = 1.0;
        gbc.fill = GridBagConstraints.BOTH;
        annosPanel.add(varCheckersSplitPane, gbc);
        final JScrollPane scrollPane1 = new JScrollPane();
        scrollPane1.setInheritsPopupMenu(false);
        varCheckersSplitPane.setLeftComponent(scrollPane1);
        varCheckerTable = new JTable();
        scrollPane1.setViewportView(varCheckerTable);
        duplicateBt = new JButton();
        duplicateBt.setIcon(new ImageIcon(getClass().getResource("/general/copy.png")));
        duplicateBt.setText("Duplicate");
        gbc = new GridBagConstraints();
        gbc.gridx = 10;
        gbc.gridy = 0;
        annosPanel.add(duplicateBt, gbc);
        editBt = new JButton();
        editBt.setIcon(new ImageIcon(getClass().getResource("/general/editItemInSection.png")));
        editBt.setText("Edit");
        gbc = new GridBagConstraints();
        gbc.gridx = 8;
        gbc.gridy = 0;
        annosPanel.add(editBt, gbc);
        addAnnoBt = new JButton();
        addAnnoBt.setHorizontalAlignment(0);
        addAnnoBt.setHorizontalTextPosition(0);
        addAnnoBt.setIcon(new ImageIcon(getClass().getResource("/general/add.png")));
        addAnnoBt.setText("");
        gbc = new GridBagConstraints();
        gbc.gridx = 2;
        gbc.gridy = 0;
        annosPanel.add(addAnnoBt, gbc);
        removeAnnoBt = new JButton();
        removeAnnoBt.setHorizontalTextPosition(0);
        removeAnnoBt.setIcon(new ImageIcon(getClass().getResource("/general/remove.png")));
        removeAnnoBt.setText("");
        gbc = new GridBagConstraints();
        gbc.gridx = 3;
        gbc.gridy = 0;
        annosPanel.add(removeAnnoBt, gbc);
        final JPanel panel1 = new JPanel();
        panel1.setLayout(new GridBagLayout());
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 1;
        gbc.gridwidth = 5;
        gbc.fill = GridBagConstraints.BOTH;
        rootPanel.add(panel1, gbc);
        panel1.setBorder(BorderFactory.createTitledBorder("General Options"));
        askToCreateSample = new JCheckBox();
        askToCreateSample.setHorizontalAlignment(11);
        askToCreateSample.setHorizontalTextPosition(11);
        askToCreateSample.setText("Ask to create sample if there is nothing to generate");
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        gbc.weightx = 1.0;
        gbc.anchor = GridBagConstraints.WEST;
        panel1.add(askToCreateSample, gbc);
        resetDefaultCfgBt = new JButton();
        resetDefaultCfgBt.setIcon(new ImageIcon(getClass().getResource("/general/reset.png")));
        resetDefaultCfgBt.setText("Reset default configuration");
        gbc = new GridBagConstraints();
        gbc.gridx = 3;
        gbc.gridy = 2;
        gbc.gridwidth = 2;
        gbc.anchor = GridBagConstraints.EAST;
        rootPanel.add(resetDefaultCfgBt, gbc);
    }

    /**
     * @noinspection ALL
     */
    public JComponent $$$getRootComponent$$$() {
        return rootPanel;
    }

    public class AnnoTableModel extends AbstractTableModel {
        /**
         * LinkedHashMap to maintain insertion order
         */
        private java.util.List<VarChecker> varCheckers;


        public java.util.List<VarChecker> getVarCheckers() {
            return varCheckers;
        }

        public final String[] COL_NAMES = {"Active", "Group", "Annotation", "Type"};
        public final Class[] COL_CLASSES = {Boolean.class, String.class, String.class, String.class};

        public AnnoTableModel() {
            this.varCheckers = new ArrayList<VarChecker>();
        }

        public void setVarCheckers(java.util.List<VarChecker> varCheckers) {
            if (varCheckers == null) {
                throw new IllegalArgumentException("varCheckers is null");
            }
            this.varCheckers.clear();
            this.varCheckers.addAll(varCheckers);
            fireTableDataChanged();
        }

        @Override
        public String getColumnName(int column) {
            return COL_NAMES[column];
        }

        @Override
        public Class<?> getColumnClass(int colIdx) {
            return COL_CLASSES[colIdx];
        }

        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            return true;
        }

        public int getRowCount() {
            return varCheckers.size();
        }

        public int getColumnCount() {
            return COL_NAMES.length;
        }

        public Object getValueAt(int rowIndex, int colIdx) {
            if (colIdx == 0) {
                return varCheckers.get(rowIndex).getActive();
            } else if (colIdx == 1) {
                return varCheckers.get(rowIndex).getGroup();
            } else if (colIdx == 2) {
                return varCheckers.get(rowIndex).getAnnotation();
            } else if (colIdx == 3) {
                return varCheckers.get(rowIndex).getType();
            }
            return "Invalid column index";
        }

        //TODO check if annotation and type are null
        @Override
        public void setValueAt(Object newVal, int rowIndex, int colIdx) {
            if (colIdx == 0) {
                Boolean val = (Boolean) newVal;
                varCheckers.get(rowIndex).setActive(val);
            } else if (colIdx == 1) {
                String val = (String) newVal;
                varCheckers.get(rowIndex).setGroup(val);
            } else if (colIdx == 2) {
                String val = (String) newVal;
                final VarChecker checker = varCheckers.get(rowIndex);
                if (!StringUtil.isEmpty(checker.getType())) {
                    checker.setAnnotation(val);
                }
            } else if (colIdx == 3) {
                String val = (String) newVal;
                final VarChecker checker = varCheckers.get(rowIndex);
                if (!StringUtil.isEmpty(checker.getAnnotation())) {
                    varCheckers.get(rowIndex).setType(val);
                }
            }
            fireTableRowsUpdated(rowIndex, rowIndex);
        }

        public void add(VarChecker varChecker) {
            int idx = varCheckers.indexOf(varChecker);
            if (idx >= 0) {
                varCheckers.add(idx + 1, varChecker);
                varCheckerTable.getSelectionModel().setLeadSelectionIndex(idx + 1);
            } else {
                varCheckers.add(varChecker);
                varCheckerTable.getSelectionModel().setLeadSelectionIndex(varCheckers.size() - 1);
            }
            fireTableDataChanged();
        }

        public void remove(int index) {
            if (index >= 0 && index < varCheckers.size()) {
                varCheckers.remove(index);
                fireTableDataChanged();
            }
        }
    }

    public CMVConfig() {
        varCheckerTableModel = new AnnoTableModel();
        varCheckerTable.setModel(varCheckerTableModel);
        varCheckerTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);


        varCheckerTable.setTableHeader(
                new JTableHeader(varCheckerTable.getColumnModel()) {
                    @Override
                    public String getToolTipText(MouseEvent e) {
                        int index = columnModel.getColumnIndexAtX(e.getPoint().x);
                        return COL_TIPS[columnModel.getColumn(index).getModelIndex()];
                    }
                });

        addVarCheckerDialog = new AddVarCheckerDialog();
        addVarCheckerDialog.setMainPage(this);
        addVarCheckerDialog.setModal(true);
        addVarCheckerDialog.pack();
        addVarCheckerDialog.setLocationRelativeTo(this.getRootComponent());

        //template
        templateEditor = EditorFactory.getInstance().createEditor(
                EditorFactory.getInstance().createDocument(""));
        templateEditor.getComponent().setPreferredSize(new Dimension(220, 100));
        templateEditor.getSettings().setFoldingOutlineShown(false);
        templateEditor.getSettings().setLineNumbersShown(true);
        templateEditor.getSettings().setLineMarkerAreaShown(false);
        templateEditor.getDocument().setReadOnly(true);
        TemplateEditorUtil.setHighlighter(templateEditor, new TemplateContext());


        varCheckersSplitPane.setRightComponent(templateEditor.getComponent());
        varCheckersSplitPane.setDividerLocation(0.8);
        varCheckersSplitPane.revalidate();

        //  LISTENERS
        addAnnoBt.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                addVarCheckerDialog.reset();
                addVarCheckerDialog.setEditMode(false);
                showAddVarChecker();
            }
        });

        removeAnnoBt.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                varCheckerTableModel.remove(varCheckerTable.getSelectionModel().getMinSelectionIndex());
            }
        });

        resetDefaultCfgBt.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                int resp =
                        JOptionPane.showConfirmDialog(getRootComponent(), "Reset configuration to default",
                                "Check my vars", JOptionPane.YES_NO_OPTION);
                if (JOptionPane.OK_OPTION == resp) {
                    ConfigImpl config = new ConfigImpl();
                    setData(config);
                }
            }
        });

        editBt.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                editCurrentVarChecker();
            }
        });


        varCheckerTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            public void valueChanged(ListSelectionEvent e) {
                int rowIdx = varCheckerTable.getSelectedRow();
                if (rowIdx >= 0 && rowIdx < varCheckerTableModel.getVarCheckers().size()) {
                    final VarChecker varChecker = varCheckerTableModel.getVarCheckers().get(rowIdx);
                    ApplicationManager.getApplication().runWriteAction(new Runnable() {
                        public void run() {
                            templateEditor.getDocument().setReadOnly(false);
                            templateEditor.getDocument().setText(varChecker.getTemplate());
                            templateEditor.getDocument().setReadOnly(true);
                        }
                    });
                }
            }
        });

        duplicateBt.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                getAnnotationTableModel().add(new VarChecker(getSelectedVarChecker()));
            }
        });


    }

    private void showAddVarChecker() {
        try {
            addVarCheckerDialog.setVisible(true);
        } catch (NullPointerException e) {
            //possible exception, don't know why...
            //ignore silentlty
            //TODO resolve this
        }
    }

    @Nullable
    public VarChecker getSelectedVarChecker() {
        //selected row is not always valid, as the model can change due to addition or removal of
        //VarChecker
        int rowIdx = varCheckerTable.getSelectedRow();
        if (rowIdx >= 0 && rowIdx < varCheckerTableModel.getVarCheckers().size()) {
            return varCheckerTableModel.getVarCheckers().get(rowIdx);
        }
        return null;
    }

    public void editCurrentVarChecker() {
        VarChecker varChecker = getSelectedVarChecker();
        if (varChecker != null) {
            addVarCheckerDialog.setVarChecker(varChecker);
            addVarCheckerDialog.setEditMode(true);
            showAddVarChecker();
        }
    }

    public AnnoTableModel getAnnotationTableModel() {
        return varCheckerTableModel;
    }

    // --------------------- METHODS ---------------------

    /**
     * Fill config parameter with visual parameter set on form
     *
     * @param config
     */
    public void getData(IConfig config) {
        config.setVarCheckers(varCheckerTableModel.getVarCheckers());
        config.setAskToCreateSample(askToCreateSample.isSelected());
    }

    public JComponent getRootComponent() {
        return this.rootPanel;
    }

    public boolean isModified(IConfig config) {
        return !varCheckerTableModel.getVarCheckers().equals(config.getVarCheckers())
                || askToCreateSample.isSelected() != config.getAskToCreateSample();

    }

    public void setData(IConfig config) {
        varCheckerTableModel.setVarCheckers(config.getVarCheckers());
        askToCreateSample.setSelected(config.getAskToCreateSample());
    }

}
